#! /usr/bin/python

####
# This is the "slave" code for jobs dispatching task. Eventually it should do the following:
# -. reads in and parse the configuration file to find "master" address and port;
# -. learns what action should take for the job from the configuration file; 
# -. connects (via socket) to the master requesting and/or reporting status of the assigned job;
#
#### 
import os
import socket
import sys
import ConfigParser
import logging
import time

###### parameter parsing #####
if len(sys.argv) < 2:
	print 'usage'
	print '-----'
	print ''
	print 'slave config_file'
	print ' '
	print ' '
	print 'Inputs:'
	print '-------'
	print 'The configuration file that discribes the jobs and the dispatching setup.'
	print 'For more information please refer to the in-line comments in the example.conf.'
	sys.exit(1)

###### read in configuration file ##### 
config=ConfigParser.SafeConfigParser()
config_file = os.path.abspath(os.path.expanduser(sys.argv[1]))
if os.path.exists(config_file):
    config.read(config_file)
else:
    print 'config_file does not exist'
    sys.exit(1)

try:
    id = config.get('MASTER','id')
except ConfigParser.NoOptionError:
    id = config_file.split('/')[-1].split('.conf')[0]  

slave_id = os.uname()[1]  # node name of the slave

action=config.get('SLAVES','action')
if action != '':
    engine_path=os.path.abspath(os.path.expanduser(os.path.split(action)[0]))
    sys.path.append(engine_path)
    engine_name=os.path.split(action)[1].split('.py')[0]
    engine=__import__(engine_name)
else:
    engine=None

logger = logging.getLogger("slave")
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
log_level = config.getint('SLAVES', 'log_level')
logger.setLevel(log_level)
if log_level <= logging.INFO:  # in debugging mode, displace logs to stdout
    ch = logging.StreamHandler()
    ch.setLevel(log_level)
    ch.setFormatter(formatter)
    logger.addHandler(ch)

###### get into the directory #####
current_dir = os.path.abspath('.')
working_dir = os.path.expanduser(config.get('SLAVES','working_directory'))
if not os.path.exists(working_dir):
    try:
        os.makedirs(working_dir)
    except:
        print 'Can not creat working directory.'
        sys.exit(1)
os.chdir(working_dir)

###### get the "master" information and make the connection #####
master_host = config.get('MASTER','host')
master_port = config.getint('MASTER','port') 

###### set up the nice level ######
nice_level = config.getint("SLAVES","nice_level")
os.nice(nice_level)

###### get the starting and ending hour
try:
    starting_time = config.getfloat("SLAVES","starting_time")
except ConfigParser.NoOptionError:
    starting_time = 22.5
try:
    ending_time = config.getfloat("SLAVES","ending_time")
except ConfigParser.NoOptionError:
    ending_time = 5.5

###### start working now! ####
data_directory=os.path.expanduser(config.get('SLAVES','data_directory'))

# a "slave" keeps on asking jobs from the "master" until the "master" says
# there is no job left
master_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
master_conn.connect((master_host,master_port))
master_conn.sendall('JOB_DONE') # JOB_DONE
logger.debug("start asking jobs from the master")
while True: 
    # get jobs from the "master"
    msg=master_conn.recv(1024)
    master_conn.shutdown(socket.SHUT_RDWR)
    master_conn.close()

    # check the time, if the current time is not (roughly) between START to END 
    # then fall asleep
    while True:
        current_day=time.localtime().tm_wday
        current_time=time.localtime().tm_hour+float(time.localtime().tm_min)/60
        if ((current_time > ending_time) and (current_time < starting_time) and (current_day not in [5,6])):        
            time.sleep(10*60)  # sleep 10 mins 
        else:
            break
   
    if not msg:
        logger.critical("this should not happen!")
        raise Exception("this should not happend!")     
    elif msg == 'NO_MORE_JOB': # NO_MORE_JOB
        logger.debug("master has no more job left.")
        break
    else:
	logger.info("recving job %s", msg)
        job=msg.split(',')
        image1=job[0]#.split('.')[0]
        image2=job[1]#.split('.')[0]
        image1_file=data_directory+'/'+image1
        image2_file=data_directory+'/'+image2
        if not (os.path.isfile(image1_file) and os.path.isfile(image2_file)):
            logger.critical("image file %s or %s does not exists", image1_file, image2_file)
            master_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            master_conn.connect((master_host,master_port))
            master_conn.send("IMAGE_FILE_NOT_EXIST")
            continue 

        my_directory=image1.split('_3DT1_')[0]+'-'+image2.split('_3DT1_')[0]+'/'+id+'/'
        if not os.path.exists(my_directory):
            try:
                os.makedirs(my_directory)
            except:
                # log 
                logger.critical("cannot create directory %s.", my_directory)
                master_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                master_conn.connect((master_host,master_port))
                master_conn.send("CANNOT_CREATE_DIR")
                raise Exception("cannot create directory %s.", my_directory)


        # creating log file for this new job
        slave_log_file = my_directory+slave_id+'.log' 
        fh = logging.FileHandler(slave_log_file,"w")
        fh.setLevel(log_level)
        fh.setFormatter(formatter)
        logger.addHandler(fh)

        # take the action and report back to "master" 
        os.chdir(my_directory)
        if engine!=None:
            try:
                retcode=engine.slave_action(image1_file,image2_file,logger)
            except:
                retcode="JOB_FAILED"
            master_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            master_conn.connect((master_host,master_port))
            master_conn.send(retcode)
        else:  
            logger.critical("no action found.")
            master_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            master_conn.connect((master_host,master_port))
            master_conn.send('CANNOT_CREATE_DIR')
            raise Exception("no action found.")

        # change back to working_dir
        os.chdir(working_dir)
        logger.removeHandler(fh)

###### clean up and die ####

master_conn.close()
os.chdir(current_dir)
